#include "LL1Parser.hpp"
#include <cctype>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>

// 全局变量，用于存储当前表达式的计算结果
double result = 0.0;

void print_vector(const std::vector<std::string> &vec) {
  for (const auto &s : vec)
    std::cout << s << " ";
  std::cout << std::endl;
}

void print_parse_table(
    const std::unordered_map<std::string, std::vector<std::string>> &table) {
  std::cout << "LL(1) Parsing Table:" << std::endl;
  for (const auto &entry : table) {
    std::cout << "[" << entry.first << "] -> ";
    print_vector(entry.second);
  }
}

void parse_input(LL1Parser &parser,
                 const std::vector<std::string> &input_tokens,
                 const std::string &start_symbol) {
  auto table = parser.get_table();
  std::stack<std::string> parse_stack;
  parse_stack.push(END_SYMNOL);
  parse_stack.push(start_symbol);

  std::stack<double> value_stack; // 用于存储操作数的栈

  std::vector<std::string> input = input_tokens;
  input.push_back(END_SYMNOL);

  size_t ip = 0;

  std::cout << "\nParsing Process:" << std::endl;
  while (!parse_stack.empty()) {
    std::string top = parse_stack.top();
    std::string current_input = input[ip];

    std::cout << "Top of stack: " << top << ", Current input: " << current_input
              << std::endl;

    if (top == current_input && top == END_SYMNOL) {
      std::cout << "Parsing successful!" << std::endl;
      std::cout << "Result: " << result << std::endl;
      std::cout << std::endl;
      return;
    }

    if (parser.terminals.count(top) || top == END_SYMNOL) {
      if (top == current_input) {
        if (top == "num") {
          // 假设输入的数字是合法的，直接转换为 double 类型
          double num = std::stod(current_input);
          value_stack.push(num);
        }
        parse_stack.pop();
        ++ip;
      } else {
        std::cout << "Match error: input symbol '" << current_input
                  << "' does not match top of stack '" << top << "'"
                  << std::endl;
        std::cout << std::endl;
        return;
      }
    } else {
      std::string key = top + current_input;
      if (table.count(key)) {
        parse_stack.pop();
        const auto &production = table.at(key);
        if (!(production.size() == 1 && production[0] == "")) {
          for (auto it = production.rbegin(); it != production.rend(); ++it)
            parse_stack.push(*it);
        }
        std::cout << "Apply production: " << top << " -> ";
        print_vector(production);

        // 处理运算符
        if (top == "Expr'" && production[0] == "+") {
          double op2 = value_stack.top();
          value_stack.pop();
          double op1 = value_stack.top();
          value_stack.pop();
          value_stack.push(op1 + op2);
        } else if (top == "Expr'" && production[0] == "-") {
          double op2 = value_stack.top();
          value_stack.pop();
          double op1 = value_stack.top();
          value_stack.pop();
          value_stack.push(op1 - op2);
        } else if (top == "Term'" && production[0] == "*") {
          double op2 = value_stack.top();
          value_stack.pop();
          double op1 = value_stack.top();
          value_stack.pop();
          value_stack.push(op1 * op2);
        } else if (top == "Term'" && production[0] == "/") {
          double op2 = value_stack.top();
          value_stack.pop();
          double op1 = value_stack.top();
          value_stack.pop();
          value_stack.push(op1 / op2);
        }
      } else {
        std::cout << "Error: no production found in parsing table for key ["
                  << key << "]" << std::endl;
        std::cout << std::endl;
        return;
      }
    }
  }

  std::cout << "Parsing failed: stack emptied unexpectedly" << std::endl;
  std::cout << std::endl;
}

std::vector<std::string> tokenize(const std::string &line) {
  std::vector<std::string> tokens;
  size_t i = 0;

  while (i < line.length()) {
    if (std::isspace(line[i])) {
      ++i;
      continue;
    }

    if (std::isalpha(line[i])) {
      std::string id;
      while (i < line.length() && std::isalnum(line[i]))
        id += line[i++];
      if (id == "odd")
        tokens.push_back("odd");
      else
        tokens.push_back("id");
    } else if (std::isdigit(line[i])) {
      std::string num;
      while (i < line.length() && std::isdigit(line[i]))
        num += line[i++];
      tokens.push_back(num); // 保留数字的原始值
    } else {
      std::string op;
      op += line[i++];
      if (i < line.length()) {
        std::string two = op + line[i];
        if (two == "<=" || two == ">=" || two == "==") {
          op = two;
          ++i;
        }
      }
      tokens.push_back(op);
    }
  }

  return tokens;
}

int main() {
  LL1Parser parser;

  parser.defines = {{"Expr", {"+", "Term", "Expr'"}},
                    {"Expr", {"-", "Term", "Expr'"}},
                    {"Expr", {"Term", "Expr'"}},
                    {"Expr'", {"+", "Term", "Expr'"}},
                    {"Expr'", {"-", "Term", "Expr'"}},
                    {"Expr'", {""}}, // ε

                    {"Term", {"Factor", "Term'"}},
                    {"Term'", {"*", "Factor", "Term'"}},
                    {"Term'", {"/", "Factor", "Term'"}},
                    {"Term'", {""}},

                    {"Factor", {"id"}},
                    {"Factor", {"num"}},
                    {"Factor", {"(", "Expr", ")"}},

                    {"Cond", {"Expr", "RelOp", "Expr"}},
                    {"Cond", {"odd", "Expr"}},

                    {"RelOp", {"="}},
                    {"RelOp", {"#"}},
                    {"RelOp", {"<"}},
                    {"RelOp", {"<="}},
                    {"RelOp", {">"}},
                    {"RelOp", {">="}}};

  parser.terminals = {"id", "num", "+", "-",  "*", "/",  "(",   ")",
                      "=",  "#",   "<", "<=", ">", ">=", "odd", END_SYMNOL};

  parser.get_first_set();
  parser.get_follow_set();
  parser.get_select_set();
  auto table = parser.get_table();

  print_parse_table(table);

  std::ifstream infile("test.txt");
  if (!infile) {
    std::cerr << "Cannot open test.txt" << std::endl;
    return 1;
  }

  std::ofstream outfile("result.txt");
  if (!outfile) {
    std::cerr << "Cannot open result.txt for writing" << std::endl;
    return 1;
  }

  std::string line;
  int line_num = 1;

  while (std::getline(infile, line)) {
    std::ostringstream header;
    header << "Line " << line_num++ << ": " << line << "\n";
    std::cout << header.str();
    outfile << header.str();

    if (auto comment_pos = line.find("//"); comment_pos != std::string::npos)
      line = line.substr(0, comment_pos);

    std::vector<std::string> tokens = tokenize(line);

    if (!tokens.empty()) {
      std::ostringstream buffer;
      std::streambuf *old_buf = std::cout.rdbuf();
      std::cout.rdbuf(buffer.rdbuf());

      parse_input(parser, tokens, "Cond");

      std::cout.rdbuf(old_buf);

      std::cout << buffer.str();
      outfile << buffer.str();
    }
  }

  outfile.close();
  return 0;
}
